package com.google.code.b0rx0r.ui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.JPopupMenu;

import sun.management.snmp.util.JvmContextFactory;

import com.google.code.b0rx0r.ColorScheme;
import com.google.code.b0rx0r.Learner;
import com.google.code.b0rx0r.program.Triggerable;

public class Keyboard extends JPanel implements Learner {
	private static int whiteKeyWidth = 12;
	private static int offset = 10;

	/* note names, used to construct the keyboard shapes */
	private static final String[] NOTE_NAMES = new String[]{"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
	
	/* a list of shapes, ordered from midi note 0 (c-1) up to midi note 127 (g9), for all the keys on the keyboard. */
	private static List<Shape> shapes = new ArrayList<Shape>();
	static {
		int currentOffset = 0;
		for (int i=0; i<128; i++) {
			String noteName = NOTE_NAMES[i % NOTE_NAMES.length];
			
      Shape s;
			
			if (noteName.endsWith("#")) { //$NON-NLS-1$
				int x0 = currentOffset - whiteKeyWidth/2;
				s = new Rectangle(x0+2, 0, whiteKeyWidth - 4, 49);
			} else {
				String nextNoteName = NOTE_NAMES[(i+1) % NOTE_NAMES.length];
				String prevNoteName = NOTE_NAMES[(i + NOTE_NAMES.length - 1) % NOTE_NAMES.length]; //i - 1 % NOTE_NAMES.length could be -1 if i is 0?

				int x0 = currentOffset;
				int x3 = x0 + whiteKeyWidth;
				int[] xs = new int[]{x0, x0, x0, x0, x3, x3, x3, x3};
				int[] ys = new int[]{0, 49, 49, 79, 79, 49, 49, 0};
				
				if (prevNoteName.endsWith("#")) { //$NON-NLS-1$
					xs[0] += whiteKeyWidth/2-2; 
					xs[1] += whiteKeyWidth/2-2; 
				}
	
				if (nextNoteName.endsWith("#") && i != 127) { //$NON-NLS-1$
					xs[6] -= whiteKeyWidth/2-2; 
					xs[7] -= whiteKeyWidth/2-2; 
				}				
				
				
				s = new Polygon(xs, ys, 8);
				
				currentOffset += whiteKeyWidth;
			}
			shapes.add(s);
		}
	}

	
	private boolean[] depressed = new boolean[shapes.size()];
	private Triggerable[] map = new Triggerable[shapes.size()];
	
	
	public Keyboard() {
		initGui();
	}

	private void initGui() {
    int wholeKeyboardWidth = 75*whiteKeyWidth + 4;
    
    Dimension d = new Dimension(wholeKeyboardWidth, 80);
		setPreferredSize(d);
		setMinimumSize(d);
		setMaximumSize(d);
		
		setDropTarget(new DropTarget(this, new DropTargetListener() {

			@Override
			public void dragEnter(DropTargetDragEvent dtde) {
				if (dtde.isDataFlavorSupported(Triggerable.DATA_FLAVOR))
					dtde.acceptDrag(DnDConstants.ACTION_MOVE);
			}

			@Override
			public void dragExit(DropTargetEvent dte) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void dragOver(DropTargetDragEvent dtde) {

				
			}

			@Override
			public void drop(DropTargetDropEvent dtde) {
				if (dtde.isDataFlavorSupported(Triggerable.DATA_FLAVOR)) {
					dtde.acceptDrop(DnDConstants.ACTION_MOVE);
					
					Point point = dtde.getLocation();
					
					int note = -1;
					
					for (int i = 0; i<shapes.size(); i++) {
						Shape s = shapes.get(i);
						if (s.contains(point)) {
							note = i;
							break;
						}
					}
					

					boolean success = false;
					
					if (note != -1) {
						try {
							Triggerable t = (Triggerable)dtde.getTransferable().getTransferData(Triggerable.DATA_FLAVOR);
							t.setNote(note);
							map[note] = t;
							success = true;
						} catch (UnsupportedFlavorException e) {
							e.printStackTrace();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}					
				  dtde.dropComplete(success);
				}
				
				repaint();//TODO replace with listener on mappings?
				
				JPopupMenu jpm = new JPopupMenu();
				jpm.add(Messages.getString("Keyboard.ContextMenu.mapToSingleKey")); //$NON-NLS-1$
				jpm.add(Messages.getString("Keyboard.ContextMenu.mapSlicesToRange")); //$NON-NLS-1$
				jpm.add(Messages.getString("Keyboard.ContextMenu.mapChromatically")); //$NON-NLS-1$
				
				jpm.show(Keyboard.this, dtde.getLocation().x, dtde.getLocation().y);
				
				
			}

			@Override
			public void dropActionChanged(DropTargetDragEvent dtde) {
				// TODO Auto-generated method stub
				
			}
		}));
	}


	@Override
	public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2 = (Graphics2D)g;
		
//		g.setColor(getBackground());
//		g.fillRect(0, 0, g.getClipBounds().width, g.getClipBounds().height);
		
		//keys
		for (int i=0;i<shapes.size();i++) {
			Shape s = shapes.get(i);
			
			Triggerable t = map[i];
			
			Color fillColor = null;
			
			if (t != null) {
				fillColor = t.getColor();
			}
			
			if (fillColor == null)
				fillColor = ColorScheme.KEYBOARD_BACKGROUND_COLOR;

			if(depressed[i]) {
				fillColor = fillColor.darker();
			} 
			

			g2.setColor(fillColor);
			g2.fill(s);

			
			g2.setColor(ColorScheme.KEYBOARD_KEY_LINE_COLOR);
			g2.draw(s);
			
		}
	}

	@Override
	public void endNote(int note) {
		depressed[note] = false;
		repaint();
	}

	@Override
	public boolean learnNote(int note) {
		depressed[note] = true;
		repaint();
		return false;
	}
	
	
	

}
